package 算法回顾;

import java.util.*;

/**
 * @description:
 * @author: 小白白
 * @create: 2021-10-01
 **/

public class No207课程表 {

    /**
     * 你这个学期必须选修 numCourses 门课程，记为 0 到 numCourses - 1 。
     * 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出，
     * 其中 prerequisites[i] = [ai, bi] ，表示如果要学习课程 ai 则 必须 先学习课程  bi 。
     * 例如，先修课程对 [0, 1] 表示：想要学习课程 0 ，你需要先完成课程 1 。
     * 请你判断是否可能完成所有课程的学习？如果可以，返回 true ；否则，返回 false 。
     *
     * 示例 1：
     * 输入：numCourses = 2, prerequisites = [[1,0]]
     * 输出：true
     * 解释：总共有 2 门课程。学习课程 1 之前，你需要完成课程 0 。这是可能的。
     * 示例 2：
     * 输入：numCourses = 2, prerequisites = [[1,0],[0,1]]
     * 输出：false
     * 解释：总共有 2 门课程。学习课程 1 之前，你需要先完成​课程 0 ；并且学习课程 0 之前，你还应先完成课程 1 。这是不可能的。
     *  
     *
     * 提示：
     * 1 <= numCourses <= 105
     * 0 <= prerequisites.length <= 5000
     * prerequisites[i].length == 2
     * 0 <= ai, bi < numCourses
     * prerequisites[i] 中的所有课程对 互不相同
     */

    public boolean canFinishGood(int numCourses, int[][] prerequisites) {

        Map<Integer,Set<Integer>> map = new HashMap<>();
        int[] arr = new int[numCourses];

        for (int[] prerequisite : prerequisites) {
            arr[prerequisite[0]]++;
            Set<Integer> set = map.getOrDefault(prerequisite[1], new HashSet<>());
            set.add(prerequisite[0]);
            map.put(prerequisite[1], set);
        }

        Deque<Integer> queue = new ArrayDeque<>();

        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == 0) {
                queue.addLast(i);
            }
        }

        while (!queue.isEmpty()) {
            Integer item = queue.removeFirst();
            numCourses--;
            Set<Integer> set = map.get(item);
            if (set != null) {
                for (Integer i : set) {
                    arr[i]--;
                    if (arr[i] == 0) {
                        queue.addLast(i);
                    }
                }
            }
        }

        return numCourses == 0;
    }

    /**
     * dfs不是很好的解法
     */
    private int[] arr;
    private Map<Integer, Set<Integer>> map = new HashMap<>();
    private Set<Integer> set = new HashSet<>();

    public boolean canFinish(int numCourses, int[][] prerequisites) {

        this.arr = new int[numCourses];

        for (int i = 0; i < numCourses; i++) {
            set.add(i);
        }

        for (int[] prerequisite : prerequisites) {
            arr[prerequisite[0]]++;
            Set<Integer> mapSet = map.getOrDefault(prerequisite[0], new HashSet<>());
            mapSet.add(prerequisite[1]);
            map.put(prerequisite[0], mapSet);
            set.remove(prerequisite[0]);
        }

        for (Map.Entry<Integer, Set<Integer>> entry : map.entrySet()) {
            this.dfs(entry.getKey(), new HashSet<>());
        }

        return set.size() == numCourses;
    }

    private boolean dfs(int numCourses,Set<Integer> path) {

        // path防止环形依赖
        if (path.contains(numCourses)) {
            return false;
        }

        if (set.contains(numCourses)) {
            return true;
        }

        Set<Integer> numSet = map.get(numCourses);

        path.add(numCourses);
        int count = 0;
        for (Integer i : numSet) {
            if(!dfs(i, path)) {
                path.remove(i);
                break;
            }
            count++;
        }
        path.remove(numCourses);

        if (count == numSet.size()) {
            this.set.add(numCourses);
            return true;
        }

        return false;
    }

    public static void main(String[] args) {
        No207课程表  n = new No207课程表();
        boolean result =
                n.canFinishGood(2,
                        new int[][]{{1,0}}
                        );
        System.out.println(result);
    }

}
